"""
CSPBuilder plugin

Provides an editor functionality for python code files

Copyright (c) 2008 Rune M. Friborg, runef@diku.dk.
See LICENSE.txt for licensing details (GPLv2).
"""

PLUGIN_NAME = "CodeEditor"

try:
    import cPickle as pickle
except:
    import pickle

import sys
import os
import shutil
import wx

from extensions import cspxml
from plugins.builder import *

import xml.dom.minidom

class PluginCodeEditor(PluginBUILDER):

    def __init__(self, pluginManager):
        PluginBUILDER.__init__(self, pluginManager, PLUGIN_NAME)

        self.home = os.getenv('USERPROFILE') or os.getenv('HOME')
        self.confPathAndFilename = self.home + '/.cspbuilder/builder.conf'

        if sys.platform == 'darwin':
            self.cmd_default = 'open -a TextEdit %f'
        elif sys.platform == 'linux2':
            self.cmd_default = 'emacs %f'
        elif sys.platform == 'win32':
            self.cmd_default = 'notepad.exe %f'


        self.cmd = self.cmd_default
        self._load_pref()

        self.pluginman = pluginManager

    def _prefmenu_activate(self, frame, event):
        
        dialog = wx.Dialog(frame, -1, "Configure editor", style = wx.DEFAULT_FRAME_STYLE | wx.WANTS_CHARS )

        win = wx.Panel(dialog, -1)

        panelSizer = wx.BoxSizer(wx.HORIZONTAL)

        t = wx.TextCtrl(win, -1, self.cmd, size=(300, -1))
        panelSizer.Add(t, 0, wx.ALL, 5)

        btnOK = wx.Button(win, wx.ID_OK, " Ok ")

        panelSizer.Add(btnOK, 0, wx.ALL | wx.ALIGN_RIGHT, 5)

        win.SetSizer(panelSizer)
        win.SetAutoLayout(True)

        dialog.SetClientSize(wx.Size(400,60))

        if (dialog.ShowModal() == wx.ID_OK):
            self.cmd = t.GetValue()

        dialog.Destroy()

        self._save_pref()


    def _prefmenu(self, frame, menu, menuid):
        menu.Append(menuid, 'Configure Editor')
        frame.Bind(wx.EVT_MENU, lambda event:self._prefmenu_activate(frame,event), id=menuid)


    def __load_all_pref(self):

        D = {}
        if os.path.exists(self.confPathAndFilename):
            # Load
            f = open(self.confPathAndFilename, 'r')
            filedata = ''
            for line in f.readlines():
                filedata += line
            D = pickle.loads(filedata)
            f.close()
        return D

    def _save_pref(self):
        
        D = self.__load_all_pref()

        D['codeeditor'] = self.cmd

        f = open(self.confPathAndFilename, "wb")
        f.write(pickle.dumps(D))
        f.close()
        
    def _load_pref(self):

        D = self.__load_all_pref()

        if (D.has_key('codeeditor')):
            self.cmd = D['codeeditor']
        else:
            self.cmd = self.cmd_default

    def _open_editor(self, filename):
        cmd = self.cmd.replace('%f', filename)
        if cmd == self.cmd:
            cmd = self.cmd + ' ' + filename

        os.popen(cmd)

    def _rcmenu(self, frame, menu, obj, menuid):
        menu.Append(menuid, 'Edit Python Code')
        frame.Bind(wx.EVT_MENU, lambda event:self._rcmenu_activate(frame,event), id=menuid)

    def _rcmenu_activate(self, frame, event):
        obj = frame.selection[0]
        self._dcmenu(frame, obj)

    def _dcmenu(self, frame, obj):

        self._load_pref()

        if (obj.cspData.has_key('xml')):

            pyFilesToEdit = []

            # Parse xml and extract code items
            dom = cspxml.parseString(obj.cspData['xml'])
            
            for process in dom.getElementsByTagName("process"):
                att = cspxml.getAttrList(process)
                name = att['name']

            for code in dom.getElementsByTagName("code"):
                att = cspxml.getAttrList(code)
                if not att['src'] in pyFilesToEdit:
                    pyFilesToEdit.append(att['src'])
            
            dom.unlink()

            for pyFile in pyFilesToEdit:
                
                # Find file
                pyFileReal = ''
                
                # find component path
                compPath = ''
                for path in self.pluginman.getList(pathlist_COMPONENTS) + [os.path.join(sys.path[0], 'Components')]:
                    dirName = name + '___csp'
                    compPath = os.path.join(path,dirName)
                    if os.path.exists(compPath):
                        break
                    compPath = ''
                    
                if not compPath:
                    return # component path not found
                
                fileName = pyFile + '.py'
                
                # if the object is an edit-version, simply edit the file in the folder
                # for that version
                processVer = obj.getVersion() 
                if processVer.startswith('e'):
                    versionDir = 'e_version%06d' % int(processVer.lstrip('e'))
                        
                    pathAndFilename = os.path.join(compPath,versionDir,fileName)
                    if os.path.exists(pathAndFilename):
                        pyFileReal = pathAndFilename
                        break
                
                # if it is not an edit-version: find folder with version,
                # copy contents to a new edit-version folder and open editor on file
                # in new folder
                else:
                    
                    # find latest edit-version
                    versionDirs = os.listdir(compPath)
                    latestVer = 0
                    eVersions = []
                    for d in versionDirs:
                        if d.startswith('e_version'):
                            eVersions.append(int(d[-6:]))
                    if len(eVersions):
                        eVersions.sort()
                        latestVer = eVersions[-1]
                    
                    # find current and new version-folders and copy content
                    oldVerDir = 'version%06d' % int(processVer)
                    oldPath = os.path.join(compPath,oldVerDir)
                    newVersion = latestVer+1
                    newVerDir = 'e_version%06d' % newVersion
                    newPath = os.path.join(compPath,newVerDir)
                    
                    dlg = wx.MessageDialog(frame,'This will create a new edit-version folder.','Creating new folder..')
                    if dlg.ShowModal() == wx.ID_OK:
                        shutil.copytree(oldPath, newPath)
                        
                        # update object
                        obj.setVersion('e'+str(newVersion))
                        obj.cspData['version'] = 'e'+str(newVersion)
                        
                        doc = xml.dom.minidom.parseString(obj.cspData['xml'])
                        for e in doc.getElementsByTagName('process'):
                            e.setAttribute('version','e'+str(newVersion))
                        obj.cspData['xml'] = doc.toxml()
                        
                        # update csp-file
                        newCspFile = os.path.join(newPath,'main.csp')
                        doc = xml.dom.minidom.parse(newCspFile)
                        for e in doc.getElementsByTagName('process'):
                            if e.parentNode.tagName == 'process':
                                e.setAttribute('version','e'+str(newVersion))
                        
                        f = open(newCspFile,"wb")
                        f.write(doc.toxml('utf-8'))
                        f.close()
                        
                        # update frame
                        frame.editVersion = True
                        frame.compVersion = newVersion
                        frame.updateTitleVersion('e'+str(newVersion))
                        
                        pyFileReal = os.path.join(newPath,fileName)
                    dlg.Destroy() 

                if pyFileReal != '':
                    print 'Opening in editor:', pyFileReal
                    self._open_editor(pyFileReal)
                 
    

def load(pluginManager):

    p = PluginCodeEditor(pluginManager)

    p.addPrefMenuElement(p._prefmenu)
    p.addRightClickElement(location_PROCESS_CODE, p._rcmenu)
    p.addDoubleClickElement(location_PROCESS_CODE, p._dcmenu)

    pluginManager.addPlugin(p)
